//
//  BufferedPaging.m
//  SimulacraDB
//
//  Created by Andre Cohen on 8/24/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BufferedPaging.h"
#import "RID.h"


@implementation BufferedPaging

- (id)initForData:(NSString *)filename blocks:(int)block  {
	[super initForData:filename blocks:block];
	
	spaces = [[[NSMutableArray alloc] init] autorelease];
	[self loadAllPages];
	
	return self;
}

-(void)close {
	int totalPages = ceil(endLocation/(blocksPerPage * 1.0));
	ByteArray *bits = [ByteArray createWithSize:totalPages * (blocksPerPage/CHAR_BIT)];
	int i,s;
	for(s=0;s<[spaces count];s++){
		NSRange curr = [[spaces objectAtIndex:s] rangeValue];
		if(s==0 && curr.location>0){
			for(i=0;i<curr.location;i++)
				[bits setBitAtLocation:i value:1];
		}else if(s>0){
			NSRange prev = [[spaces objectAtIndex:s-1] rangeValue];
			for(i=prev.location + prev.length;i<curr.location;i++)
				[bits setBitAtLocation:i value:1];
		}
	}
	
	if([spaces count]>0){
		NSRange last = [[spaces lastObject] rangeValue];
		if(last.location+last.length<endLocation){
			for(i=last.location+last.length;i<endLocation;i++)
				[bits setBitAtLocation:i value:1];
		}
	}
	
	if([spaces count]==0 && endLocation>0){
		for(i=0;i<endLocation;i++)
			[bits setBitAtLocation:i value:1];
	}
	
	[pages write:bits at:0];
	
	[super close];
}

- (void)loadAllPages {
	int count = [self pageCount];
	int i = 0, b = 0;
	int start = -1;
	
	for(i=0;i<count;i++){
		ByteArray *page = [self getPageNumber:i];
		for(b=0;b<[page bitCount];b++){
			if(start>=0){
				if([page getBitFromLocation:b]==TRUE){
					NSValue *r = [[NSValue valueWithRange:NSMakeRange(start,(i*blocksPerPage)+b-start)] autorelease];
					[spaces addObject:r];
					start = -1;
				}
			}else{
				if([page getBitFromLocation:b]==FALSE){
					start = (i*blocksPerPage)+b;
				}
			}
		}
	}
	if(start>0){
		NSValue *r = [[NSValue valueWithRange:NSMakeRange(start,(i*blocksPerPage)-start)] autorelease];
		[spaces addObject:r];
	}
	endLocation = count * blocksPerPage;
}


- (RID *)findBlockWithSize:(int)blocks {
	int i;
	for(i=0;i<[spaces count];i++){
		NSRange r = [[spaces objectAtIndex:0] rangeValue];
		if(blocks<=r.length){
			return [RID createWithPage:floor(r.location/(blocksPerPage*1.0)) offset:r.location%blocksPerPage];
		}
	}
	return [RID createWithPage:floor(endLocation/(blocksPerPage * 1.0)) offset:endLocation%blocksPerPage];
}

- (void)writeRecord:(Record *)record {
	int blocksNeeded = ceil([record size]/(blockSize * 1.0));
	
	RID *location = [self findBlockWithSize:blocksNeeded];
	[[record wrapper] setRid:location];	
	
	// Update paging
	[self markBlocksUsedFrom:location blocks:blocksNeeded];
	
	// Save record
	[self writeRecordData:record];
	
}

- (void)removeRecord:(Record *)record {
	int location = [[[record wrapper] rid] page] * blocksPerPage + [[[record wrapper] rid] offset];
	int size = ceil([record size]/(blockSize  * 1.0));
	
	int i; 
	BOOL added = NO;
	for(i=0;i<[spaces count] && !added;i++){
		NSRange r = [[spaces objectAtIndex:i] rangeValue];
		if(location>r.location){
			[spaces insertObject:[NSValue valueWithRange:NSMakeRange(location, size)] atIndex:i+1];
			added = YES;
		}
	}
	
	if(!added){
		[spaces insertObject:[NSValue valueWithRange:NSMakeRange(location, size)] atIndex:0];
	}
	
	for(i=0;i<[spaces count];i++){
		if(i+1<[spaces count]){
			NSRange curr = [[spaces objectAtIndex:i] rangeValue];
			NSRange next = [[spaces objectAtIndex:i+1] rangeValue];
			if(curr.location + curr.length == next.location){
				next.location = curr.location;
				next.length = next.length + curr.length;
				[spaces removeObjectAtIndex:i];
				[spaces removeObjectAtIndex:i];
				[spaces insertObject:[NSValue valueWithRange:next] atIndex:i];
				i = -1;
			}
		}
	}

}

- (Record *)getRecordAt:(Record *)record {
	int i;
	NSRange r;
	BOOL past = NO;
	for(i=0;i<[spaces count] && !past;i++){
		r = [[spaces objectAtIndex:i] rangeValue];
		if([self isRID:[[record wrapper] rid] inRange:r])
			return nil;
		past = [self isRID:[[record wrapper] rid] pastRange:r];
	}
	
	return [self readRecordData:record];
}
		   
- (BOOL)isRID:(RID *)rid inRange:(NSRange)range {
	int loc = [rid page]*blocksPerPage + [rid offset];
	return loc>=range.location && loc<(range.location+range.length);
}
- (BOOL)isRID:(RID *)rid pastRange:(NSRange)range {
	int loc = [rid page]*blocksPerPage + [rid offset];
	return loc>=(range.location+range.length);
}

- (void)markBlocksUsedFrom:(RID *)location blocks:(int)length {
	int s = [location page]*blocksPerPage + [location offset];

	int i = 0;
	for(i=0;i<[spaces count];i++){
		NSRange r = [[spaces objectAtIndex:i] rangeValue];
		if(r.location==s){
			if(r.length==length){
				[spaces removeObjectAtIndex:i];
				return;
			}else{
				r.location = r.location + length;
				r.length = r.length - length;
				[spaces removeObjectAtIndex:i];
				[spaces insertObject:[NSValue valueWithRange:r] atIndex:i];
				return;
			}
		}
	}
	endLocation = endLocation + length;
}

- (NSString *)description {
	return [spaces description];
}

@end
